#!/usr/bin/env python
import datetime
import time
import re
import sys
import getopt
import os
import subprocess
import signal
from dateutil.relativedelta import relativedelta


#global variable definition
is_debug = False
log_file = ""
blocked = True
output_mode = False
process = False
donau_command = ""
option_dic = {}


def split_num_letters(astr):
    nums, letters = "", ""
    for i in astr:
        if i.isdigit():
            nums = nums + i
        elif i.isspace():
            pass
        else:
            letters = letters + i
    return nums, letters


def convert_now(begin_time):
    time_converted = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
    if begin_time == "now":
        return time_converted
    time_count, time_unit = split_num_letters(begin_time[4:])
    if time_unit == "" or time_unit == "seconds":
        time_converted = (datetime.datetime.now() + relativedelta(seconds=int(time_count))).strftime(
            '%Y/%m/%d %H:%M:%S')
    elif time_unit == "minutes":
        time_converted = (datetime.datetime.now() + relativedelta(minutes=int(time_count))).strftime(
            '%Y/%m/%d %H:%M:%S')
    elif time_unit == "hours":
        time_converted = (datetime.datetime.now() + relativedelta(hours=int(time_count))).strftime('%Y/%m/%d %H:%M:%S')
    elif time_unit == "days":
        time_converted = (datetime.datetime.now() + relativedelta(days=int(time_count))).strftime('%Y/%m/%d %H:%M:%S')
    elif time_unit == "weeks":
        time_converted = (datetime.datetime.now() + relativedelta(weeks=int(time_count))).strftime('%Y/%m/%d %H:%M:%S')
    return time_converted


def converthh_mm_ss(hh_mm_ss):
    if hh_mm_ss.count(':') == 1:
        hh_mm_ss += ':00'
    time_day = time.strftime('%Y/%m/%d', time.localtime())
    time_str = time_day + " " + hh_mm_ss
    time_form = time.strptime(time_str, '%Y/%m/%d %H:%M:%S')
    if int(time.mktime(time_form)) - int(time.mktime(datetime.datetime.now().timetuple())) < 0:
        time_tomorrow_r = datetime.datetime.now() + datetime.timedelta(days=1)
        time_tomorrow = time.strftime('%Y/%m/%d', time_tomorrow_r.timetuple())
        time_str = time_tomorrow + " " + hh_mm_ss
    return time_str


def time_is_match(time_out):
    is_match=False
    pattern1 = re.compile("(\d+-){0,1}(\d+:){0,2}(\d+){0,1}").match(time_out)
    if pattern1 is not None and pattern1.group(0) == time_out:
        is_match = True
    return is_match


def convert_timeout(time_out):
    timeout_converted = ''
    if '-' in time_out and ':' in time_out:
        time_out_be, time_out_af = time_out.strip().split('-')
        if time_out_af.count(':') == 2:
            timeout_hms = time_out_af.strip().split(':')
            seconds = int(timeout_hms[2]) % 60
            minutes = (int(timeout_hms[1]) + int(timeout_hms[2]) // 60) % 60
            hours = int(time_out_be) * 24 + int(timeout_hms[0]) + (int(timeout_hms[1]) + int(timeout_hms[2]) // 60) // 60
            timeout_converted += str(hours) + 'h' + str(minutes) + 'm' + str(seconds) + 's'
        elif time_out_af.count(':') == 1:
            timeout_hm = time_out_af.strip().split(':')
            minutes = int(timeout_hm[1]) % 60
            hours = int(time_out_be) * 24 + int(timeout_hm[0]) + int(timeout_hm[1]) // 60
            timeout_converted += str(hours) + 'h' + str(minutes) + 'm'
    elif '-' in time_out:
        time_out_d, time_out_h = time_out.strip().split('-')
        hours = int(time_out_d) * 24 + int(time_out_h)
        timeout_converted += str(hours) + 'h'
    elif ':' in time_out:
        if time_out.count(':') == 2:
            timeout_hms = time_out.strip().split(':')
            seconds = int(timeout_hms[2]) % 60
            minutes = (int(timeout_hms[1]) + int(timeout_hms[2]) // 60) % 60
            hours = int(timeout_hms[0]) + (int(timeout_hms[1]) + int(timeout_hms[2]) // 60) // 60
            timeout_converted += str(hours) + 'h' + str(minutes) + 'm' + str(seconds) + 's'
        elif time_out.count(':') == 1:
            timeout_ms = time_out.strip().split(':')
            seconds = int(timeout_ms[1]) % 60
            minutes = (int(timeout_ms[0]) + int(timeout_ms[1]) // 60) % 60
            if (int(timeout_ms[0]) + int(timeout_ms[1]) // 60) // 60 > 0:
                hours = (int(timeout_ms[0]) + int(timeout_ms[1]) // 60) // 60
                timeout_converted += str(hours) + 'h' + str(minutes) + 'm' + str(seconds) + 's'
            else:
                timeout_converted += str(minutes) + 'm' + str(seconds) + 's'
    else:
        if int(time_out) // 60 > 0:
            minutes = int(time_out) % 60
            hours = int(time_out) // 60
            timeout_converted += str(hours) + 'h' + str(minutes) + 'm'
        else:
            timeout_converted += time_out + 'm'
    return timeout_converted


def convert_day(begin_time):
    time_form = datetime.date.today()
    if re.match('\d{2}\d{2}\d{2}', begin_time):
        begin_time_r = begin_time[:2] + '/' + begin_time[2:4] + '/' + begin_time[4:]
        time_form = time.strptime(begin_time_r, '%m/%d/%y')
    elif re.match('\d{2}/\d{2}/\d{2}', begin_time):
        time_form = time.strptime(begin_time, '%m/%d/%y')
    elif re.match('\d{4}-\d{2}-\d{2}', begin_time):
        time_form = time.strptime(begin_time, '%Y-%m-%d')
    return time.strftime('%Y/%m/%d %H:%M:%S', time_form)

    
#check whether the begintime variable format is legal
def begintime_is_match(begintime):
    is_begintime = False
    if begintime in ('today', 'tomorrow', 'now'):
        is_begintime = True
    elif "now" in begintime:
        if begintime.startswith("now+") and len(begintime) > 4:
            index_end = 4
            for i in range(len(begintime[4:])):
                if not begintime[4 + i].isdigit():
                    index_end = 4 + i
                    break
            if index_end == 4:
                is_begintime = True
            elif begintime[index_end:] in ("", "minutes", "hours", "days", "weeks"):
                is_begintime = True
    elif begintime.strip().count(' ') == 0:
        pattern1 = re.compile("T{0,1}(\d{4}-\d{2}-\d{2}){0,1}T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1}").match(begintime)
        pattern2 = re.compile("T{0,1}(\d{2}/\d{2}/\d{2}){0,1}T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1}").match(begintime)
        pattern3 = re.compile("T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1}(\d{4}-\d{2}-\d{2}){0,1}T{0,1}").match(begintime)
        pattern4 = re.compile("T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1}(\d{2}/\d{2}/\d{2}){0,1}T{0,1}").match(begintime)
        pattern5 = re.compile("T{0,1}\d{4}-\d{2}-\d{2}T{0,1}").match(begintime)
        pattern6 = re.compile("T{0,1}\d{2}/\d{2}/\d{2}T{0,1}").match(begintime)
        pattern7 = re.compile("T{0,1}\d{6}T{0,1}").match(begintime)
        if pattern1 is not None and pattern1.group(0) == begintime:
            is_begintime = True
        elif pattern2 is not None and pattern2.group(0) == begintime:
            is_begintime = True
        elif pattern3 is not None and pattern3.group(0) == begintime:
            is_begintime = True
        elif pattern4 is not None and pattern4.group(0) == begintime:
            is_begintime = True
        elif pattern5 is not None and pattern5.group(0) == begintime:
            is_begintime = True
        elif pattern6 is not None and pattern6.group(0) == begintime:
            is_begintime = True
        elif pattern7 is not None and pattern7.group(0) == begintime:
            is_begintime = True
    elif begintime.strip().count(' ') > 0:
        before, after = begintime.strip().split()
        if ':' in after:
            before, after = after, before
        begintime = ' '.join([before, after])
        pattern1 = re.compile("T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1} T{0,1}\d{2}/\d{2}/\d{2}T{0,1}").match(begintime)
        pattern2 = re.compile("T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1} T{0,1}\d{4}-\d{2}-\d{2}T{0,1}").match(begintime)
        pattern3 = re.compile("T{0,1}\d{2}(:\d{2}){1,2}(AM|PM)*T{0,1} T{0,1}\d{6}T{0,1}").match(begintime)
        if pattern1 is not None and pattern1.group(0) == begintime:
            is_begintime = True
        elif pattern2 is not None and pattern2.group(0) == begintime:
            is_begintime = True
        elif pattern3 is not None and pattern3.group(0) == begintime:
            is_begintime = True
    return is_begintime

    
#begintime parameter format is uniformly converted to yyyy/MM/dd HH24:mm:ss format
def time_format(input_time):
    try:
        if "AM" in input_time or "PM" in input_time:
            index = input_time.find(':')
            if index == -1:
                raise Exception
            elif index - 2 >= 0:
                if int(input_time[index - 2:index]) > 12:
                    raise Exception
                elif index - 2 == 0 and "AM" in input_time and int(input_time[index - 2:index]) == 12:
                    input_time = input_time.replace("AM", "")
                    input_time = "00" + input_time[2:]
                elif index - 2 == 0 and "AM" in input_time and int(input_time[index - 2:index]) < 12:
                    input_time = input_time.replace("AM", "")
                elif index - 2 > 0 and "AM" in input_time and int(input_time[index - 2:index]) == 12:
                    input_time = input_time.replace("AM", "")
                    input_time = input_time[:index - 2] + "00" + input_time[index:]
                elif index - 2 > 0 and "AM" in input_time and int(input_time[index - 2:index]) < 12:
                    input_time = input_time.replace("AM", "")
                elif "PM" in input_time and int(input_time[index - 2:index]) == 12:
                    input_time = input_time.replace("PM", "")
                elif index - 2 == 0 and "PM" in input_time and int(input_time[index - 2:index]) < 12:
                    input_time = input_time.replace("PM", "")
                    input_time = str(int(input_time[:2]) + 12) + input_time[2:]
                elif index - 2 > 0 and "PM" in input_time and int(input_time[index - 2:index]) < 12:
                    input_time = input_time.replace("PM", "")
                    input_time = input_time[:index - 2] + str(int(input_time[index - 2:index]) + 12) + input_time[
                                                                                                       index:]
        if "T" in input_time:
            input_time = input_time.replace('T', ' ')
        if input_time.strip().count(' ') == 0 and input_time.count(':') > 0 \
                and ('-' in input_time or '/' in input_time):
            index = input_time.find(':')
            if index == -1:
                raise Exception
            elif index - 2 == 0:
                if '-' in input_time:
                    index = input_time.find('-')
                    if index == -1:
                        raise Exception
                    elif index - 4 >= 0:
                        input_time = input_time[:index - 4] + ' ' + input_time[index - 4:]
                elif '/' in input_time:
                    index = input_time.find('/')
                    if index == -1:
                        raise Exception
                    elif index - 2 >= 0:
                        input_time = input_time[:index - 2] + ' ' + input_time[index - 2:]
            elif index - 2 > 0:
                input_time = input_time[:index - 2] + ' ' + input_time[index - 2:]
        if input_time == "today" or input_time == "now":
            return (datetime.datetime.now() + relativedelta(seconds=1)).strftime('%Y/%m/%d %H:%M:%S')
        elif input_time == "tomorrow":
            return (datetime.datetime.now() + relativedelta(days=1)).strftime('%Y/%m/%d %H:%M:%S')
        elif "now" in input_time and input_time.startswith("now+") and len(input_time) > 4:
            return convert_now(input_time)
        elif input_time.strip().count(' ') == 0 and input_time.count(':') == 0:
            return convert_day(input_time)
        elif input_time.strip().count(' ') == 0 and input_time.count(':') > 0 \
                and '-' not in input_time and '/' not in input_time:
            return converthh_mm_ss(input_time)
        elif input_time.strip().count(' ') > 0:
            before, after = input_time.strip().split()
            if ':' in after:
                before, after = after, before
            input_time = ' '.join([before, after])
            pattern1 = re.compile("\d{2}:\d{2} \d{2}/\d{2}/\d{2}").match(input_time)
            pattern2 = re.compile("\d{2}:\d{2} \d{4}-\d{2}-\d{2}").match(input_time)
            pattern3 = re.compile("\d{2}:\d{2}:\d{2} \d{2}/\d{2}/\d{2}").match(input_time)
            pattern4 = re.compile("\d{2}:\d{2}:\d{2} \d{4}-\d{2}-\d{2}").match(input_time)
            pattern5 = re.compile("\d{2}(:\d{2}){1,2} \d{6}").match(input_time)
            if pattern1 is not None and pattern1.group(0) == input_time:
                time_form = datetime.datetime.strptime(input_time, '%H:%M %m/%d/%y')
                return time_form.strftime('%Y/%m/%d %H:%M:%S')
            elif pattern2 is not None and pattern2.group(0) == input_time:
                time_form = datetime.datetime.strptime(input_time, '%H:%M %Y-%m-%d')
                return time_form.strftime('%Y/%m/%d %H:%M:%S')
            elif pattern3 is not None and pattern3.group(0) == input_time:
                time_form = datetime.datetime.strptime(input_time, '%H:%M:%S %m/%d/%y')
                return time_form.strftime('%Y/%m/%d %H:%M:%S')
            elif pattern4 is not None and pattern4.group(0) == input_time:
                time_form = datetime.datetime.strptime(input_time, '%H:%M:%S %Y-%m-%d')
                return time_form.strftime('%Y/%m/%d %H:%M:%S')
            elif pattern5 is not None and pattern5.group(0) == input_time:
                before, after = input_time.strip().split()
                begin_time_r = after[:2] + '/' + after[2:4] + '/' + after[4:]
                time_form = time.strptime(begin_time_r, '%m/%d/%y')
                time_day = time.strftime('%Y/%m/%d', time_form)
                if before.count(':') == 1:
                    before += ':00'
                time_str = time_day + " " + before
                time_form = time.strptime(time_str, '%Y/%m/%d %H:%M:%S')
                if int(time.mktime(time_form)) - int(time.mktime(datetime.datetime.now().timetuple())) < 0:
                    time_tomorrow_r = datetime.datetime.now() + datetime.timedelta(days=1)
                    time_tomorrow = time.strftime('%Y/%m/%d', time_tomorrow_r.timetuple())
                    time_str = time_tomorrow + " " + after
                return time_str
    except:
        error_info = 'Invalid time specification:' + input_time + '\n' + \
                     'srun: error: Invalid --begin specification'
        print(error_info)
        log(error_info)
        sys.exit()
    return datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')

    
#check the format of the depdency parameter
def match_dependency(condition):
    is_dependency = False
    if condition.count(':') == 1:
        input_s = condition.strip().split(':')
        title, content = input_s[0], input_s[1]
        is_title = title in ('afterok', 'afternotok', 'after')
        is_content = True
        for job in content.strip().split(','):
            if not job.isdigit():
                is_content = False
        is_dependency = is_title and is_content
    return is_dependency


def split_dependency(dependency):
    task_status = {'afterok': 'SUCCEEDED', 'afternotok': 'FAILED', 'after': 'ENDED'}
    input_s = dependency.strip().split(':')
    title, content = input_s[0], input_s[1]
    jobs = content.strip().split(',')
    operation = ""
    if len(jobs) == 1:
        operation += '"' + jobs[0] + '=' + task_status[title] + '"'
    elif len(jobs) > 1:
        operation += '"' + jobs[0] + '=' + task_status[title] + ';'
        for i in range(1, len(jobs) - 1):
            operation += jobs[i] + '=' + task_status[title] + ';'
        operation += jobs[len(jobs) - 1] + '=' + task_status[title] + '"'
    return operation


def get_invalid_option(err_message):
    if "not recognized" in err_message:
        options = re.findall(r'option (.*) not recognized', err_message)
        if len(options) == 1:
            return options[0]
    return ""


def print_invalid_info(option):
    print("srun: invalid option '" + option + "'")
    log("srun: invalid option '" + option + "'")
    print('''Try "srun --help" for more information''')
    log('''Try "srun --help" for more information''')


def handle_parameter_exception():
    global option_dic
    for opt_name, opt_value in option_dic.items():
        if opt_name == 'BEGIN':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--begin" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
            if not begintime_is_match(opt_value):
                error_info = 'Invalid time specification:' + opt_value + '\n' + \
                             'srun: error: Invalid --begin specification'
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_CPUS_PER_TASK':
            if not opt_value.isdigit():
                print('srun: error: Invalid numeric value "' + opt_value + '" for --cpus-per-task.')
                log('srun: error: Invalid numeric value "' + opt_value + '" for --cpus-per-task.')
                sys.exit()
        elif opt_name == 'COMMENT':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--comment" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SLURM_DEPENDENCY':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--dependency" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
            elif not match_dependency(opt_value):
                print('srun: error: Batch job submission failed: Job dependency problem')
                log('srun: error: Batch job submission failed: Job dependency problem')
                sys.exit()
        elif opt_name == 'SLURM_WORKING_DIR':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--chdir" requires an argument
 Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_EXPORT':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--export" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_ERROR':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--error" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SLURM_EPILOG':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--epilog" requires an argument
 Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'GID':
            if not opt_value.isdigit():
                print('srun: error: Invalid numeric value "' + opt_value + '" for --gid.')
                log('srun: error: Invalid numeric value "' + opt_value + '" for --gid.')
                sys.exit()
        elif opt_name == 'SRUN_JOB_NAME':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--job_name" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SLURM_NTASKS':
            if not opt_value.isdigit():
                print('srun: error: Invalid numeric value "' + opt_value + '" for --ntasks.')
                log('srun: error: Invalid numeric value "' + opt_value + '" for --ntasks.')
                sys.exit()
        elif opt_name == 'SRUN_OUTPUT':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--output" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_OPEN_MODE':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--open-mode" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_PARTITION':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--partition" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'PRIORITY':
            if not opt_value.isdigit() or int(opt_value) < 1 or int(opt_value) > 9999:
                print('srun: error: Invalid numeric value "' + opt_value + '" for --priority.')
                log('srun: error: Invalid numeric value "' + opt_value + '" for --priority.')
                sys.exit()
        elif opt_name == 'SLURM_PROLOG':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--prolog" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'PTY':
            pass
        elif opt_name == 'SRUN_TIMELIMIT':
            if not time_is_match(opt_value):
                print('srun: error: Invalid --time specification')
                log('srun: error: Invalid --time specification')
                sys.exit()
        elif opt_name == 'SLURM_EXCLUSIVE':
            pass
        elif opt_name == 'SRUN_GPUS_PER_TASK':
            if not opt_value.isdigit():
                print('srun: error: Invalid numeric value "' + opt_value + '" for --gpus-per-task.')
                log('srun: error: Invalid numeric value "' + opt_value + '" for --gpus-per-task.')
                sys.exit()
        elif opt_name == 'SRUN_NODELIST':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--nodelist" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()
        elif opt_name == 'SRUN_EXCLUDE':
            if not opt_value or opt_value.endswith('.sh'):
                error_info = '''srun: option "--exclude" requires an argument
Try 'srun --help' for more info'''
                print(error_info)
                log(error_info)
                sys.exit()


def update_dict(opts):
    global option_dic
    for opt_name, opt_value in opts:
        if opt_name == '-h' or opt_name == '--help':
            option_dic["HELP"] = opt_value
        if opt_name == '--usage':
            option_dic["USAGE"] = opt_value
        if opt_name == '-b' or opt_name == '--begin':
            option_dic["BEGIN"] = opt_value
        if opt_name == '-c' or opt_name == '--cpus-per-task':
            option_dic["SRUN_CPUS_PER_TASK"] = opt_value
        if opt_name == '--comment':
            option_dic["COMMENT"] = opt_value
        if opt_name == '-d' or opt_name == '--dependency':
            option_dic["SLURM_DEPENDENCY"] = opt_value
        if opt_name == '-D' or opt_name == '--chdir':
            option_dic["SLURM_WORKING_DIR"] = opt_value
        if opt_name == '--export':
            option_dic["SLURM_EXPORT_ENV"] = opt_value
        if opt_name == '-e' or opt_name == '--error':
            option_dic["SLURM_STDERRMODE"] = opt_value
        if opt_name in ('--gid'):
            option_dic["GID"] = opt_value
        if opt_name == '--epilog':
            option_dic["SLURM_EPILOG"] = opt_value
        if opt_name == '-J' or opt_name == '--job_name':
            option_dic["SLURM_JOB_NAME"] = opt_value
        if opt_name == '-n' or opt_name == '--ntasks':
            option_dic["SLURM_NTASKS"] = opt_value
        if opt_name == '-o' or opt_name == '--output':
            option_dic["SLURM_STDOUTMODE"] = opt_value
        if opt_name == '--open-mode':
            option_dic["SLURM_OPEN_MODE"] = opt_value
        if opt_name == '-p' or opt_name == '--partition':
            option_dic["SLURM_PARTITION"] = opt_value
        if opt_name == '--priority':
            option_dic["PRIORITY"] = opt_value
        if opt_name == '--prolog':
            option_dic["SLURM_PROLOG"] = opt_value
        if opt_name == '--pty':
            option_dic["PTY"] = opt_value
        if opt_name == '-t' or opt_name == '--time':
            option_dic["SLURM_TIMELIMIT"] = opt_value
        if opt_name == '--exclusive':
            option_dic["SLURM_EXCLUSIVE"] = opt_value
        if opt_name == '--gpus-per-task':
            option_dic["SLURM_GPUS_PER_TASK"] = opt_value
        if opt_name == '-w' or opt_name == '--nodelist':
            option_dic["SRUN_NODELIST"] = opt_value
        if opt_name == '-x' or opt_name == '--exclude':
            option_dic["SRUN_EXCLUDE"] = opt_value


def log(content):
    global is_debug
    if is_debug:
        global log_file
        f = open(log_file, 'a+')
        f.write(content + '\n')
        f.close()


def init_log_file_name():
    global log_file
    file_name = '%s.%d.%d' % ('srun', os.getuid(), os.getpid())
    log_file = os.path.join('/tmp/', file_name)


# init options from environment
def init_config_with_env():
    global is_debug
    global option_dic
    if os.getenv('SLURM_TO_DONAU_DEBUG') is not None \
            and os.getenv('SLURM_TO_DONAU_DEBUG').lower() == 'true':
        is_debug = True
        init_log_file_name()
    else:
        is_debug = False
    if os.environ.get("SRUN_CPUS_PER_TASK") is not None:
        option_dic["SRUN_CPUS_PER_TASK"] = os.environ.get("SRUN_CPUS_PER_TASK")
    if os.environ.get("SLURM_DEPENDENCY") is not None:
        option_dic["SLURM_DEPENDENCY"] = os.environ.get("SLURM_DEPENDENCY")
    if os.environ.get("SLURM_WORKING_DIR") is not None:
        option_dic["SLURM_WORKING_DIR"] = os.environ.get("SLURM_WORKING_DIR")
    if os.environ.get("SLURM_EXPORT_ENV") is not None:
        option_dic["SLURM_EXPORT_ENV"] = os.environ.get("SLURM_EXPORT_ENV")
    if os.environ.get("SLURM_STDERRMODE") is not None:
        option_dic["SLURM_STDERRMODE"] = os.environ.get("SLURM_STDERRMODE")
    if os.environ.get("SLURM_EPILOG") is not None:
        option_dic["SLURM_EPILOG"] = os.environ.get("SLURM_EPILOG")
    if os.environ.get("SLURM_JOB_NAME") is not None:
        option_dic["SLURM_JOB_NAME"] = os.environ.get("SLURM_JOB_NAME")
    if os.environ.get("SLURM_NTASKS") is not None:
        option_dic["SLURM_NTASKS"] = os.environ.get("SLURM_NTASKS")
    if os.environ.get("SLURM_STDOUTMODE") is not None:
        option_dic["SLURM_STDOUTMODE"] = os.environ.get("SLURM_STDOUTMODE")
    if os.environ.get("SLURM_OPEN_MODE") is not None:
        option_dic["SLURM_OPEN_MODE"] = os.environ.get("SLURM_OPEN_MODE")
    if os.environ.get("SLURM_PARTITION") is not None:
        option_dic["SLURM_PARTITION"] = os.environ.get("SLURM_PARTITION")
    if os.environ.get("SLURM_PROLOG") is not None:
        option_dic["SLURM_PROLOG"] = os.environ.get("SLURM_PROLOG")
    if os.environ.get("SLURM_TIMELIMIT") is not None:
        option_dic["SLURM_TIMELIMIT"] = os.environ.get("SLURM_TIMELIMIT")
    if os.environ.get("SLURM_GPUS_PER_TASK") is not None:
        option_dic["SLURM_GPUS_PER_TASK"] = os.environ.get("SLURM_GPUS_PER_TASK")


def check_donau_cli_env():
    sched_cli_home = os.getenv('CCSCHEDULER_CLI_HOME')
    ccs_cli_home = os.getenv('CCS_CLI_HOME')
    if sched_cli_home is None or ccs_cli_home is None:
        print('cli env home is not configured, '
              'source configure \'profile.env\'(bash) or \'cshrc.env\'(csh) first')
        sys.exit(1)
    global djob_path
    djob_path = os.path.join(ccs_cli_home, 'bin', 'djob')
    if not os.path.exists(djob_path):
        print('djob path is not exist,'
              'source configure \'profile.env\'(bash) or \'cshrc.env\'(csh) first')
        sys.exit(1)


def get_help_info():
    help_info = '''Usage: srun [OPTIONS(0)... [executable(0) [args(0)...]]] [ : [OPTIONS(N)...]] executable(N) [args(N)...]

Parallel run options:
-b, --begin=time                              defer job until HH:MM MM/DD/YY
-c, --cpus-per-task=ncpus                     number of cpus required per task
    --comment=name                            arbitrary comment
-d, --dependency=type:jobid                   defer job until condition on jobid is satisfied
-D, --chdir=path                              change remote current working directory
    --export=env_vars|NONE                    environment variables passed to launcher with
                                              optional values or NONE (pass no variables)
-e, --error=err                               location of stderr redirection
    --epilog=program                          run "program" after launching job
    --gid=group_id                            group ID to run job 
-J, --job-name=jobname                        name of job
-n, --ntasks=ntasks                           number of tasks to run
-o, --output=out                              location of stdout redirection
    --open-mode={append|truncate}             mode of open the output and error files
-p, --partition=partition                     partition requested
    --priority=value                          set the priority of the job to value
    --prolog=program                          run "program" before launching job
    --pty                                     run interactive job
-t, --time=minutes                            time limit

Constraint options:
-w, --nodelist=hosts...                       request a specific list of hosts
-x, --exclude=hosts...                        exclude a specific list of hosts 

Consumable resources related options:
    --exclusive                               for job allocation, this allocates nodes in
                                              in exclusive mode

GPU scheduling options:
    --gpus-per-task=n                         number of GPUs required per spawned task

Help options:
    --help                                    show this help message
    --usage                                   display brief usage message'''
    return help_info


def get_usage_info():
    usage_info = '''Usage: srun [-n ntasks] [-o out][-b "HH:MM MM/DD/YY"]
            [--open-mode={append|truncate}] [-e err]
            [-c ncpus] [-p partition] [--priority=value][--pty]
            [-t minutes] [-D path][-J jobname][--gid=group]
            [--dependency=type:jobid][--comment=name]
            [--nodelist=hosts][--exclude=hosts][--export=env_vars]
            [--prolog=fname] [--epilog=fname][--exclusive]
            [--gpus-per-task=n]
            executable [args...]'''
    return usage_info

    
if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    if os.getuid() == 0:
        print('Permission denied. The root user is not allowed to operate.')
        log('Permission denied. The root user is not allowed to operate.')
        sys.exit(1)

    check_donau_cli_env()
    init_config_with_env()
    short_options = '-b:-c:-d:-D:-e:-J:-n:-o:-p:-t:-w:-x:'
    long_options = ['begin=', 'cpus-per-task=', 'comment=', 'dependency=', 'chdir=',
                    'export=','error=', 'gid=', 'epilog=', 'job-name=', 'ntasks=',
                    'output=', 'open-mode=', 'partition=', 'priority=','prolog=',
                    'pty', 'time=', 'exclusive', 'gpus-per-task=','nodelist=',
                    'exclude=', 'usage', 'help']
    try:
        opts, args = getopt.getopt(sys.argv[1:], short_options, long_options)
        # options from command have higher priority than environment
        update_dict(opts)
        if "HELP" in option_dic:
            print(get_help_info())
            log(get_help_info())
            sys.exit()
        if "USAGE" in option_dic:
            print(get_usage_info())
            log(get_usage_info())
            sys.exit()
        handle_parameter_exception()
        donau_command += "dsub "
        if "PTY" in option_dic:
            blocked = False
        if "SLURM_OPEN_MODE" in option_dic and option_dic["SLURM_OPEN_MODE"] == 'append':
            output_mode = True
        if "SLURM_NTASKS" in option_dic and int(option_dic["SLURM_NTASKS"]) > 1:
            if blocked:
                print('error: block job not supported Array job(n>1)')
                log('error: block job not supported Array job(n>1)')
                sys.exit()
            else:
                print('error: interactive job not supported Array job(n>1)')
                log('error: interactive job not supported Array job(n>1)')
                sys.exit()
        if blocked:
            donau_command += "-Kco "
        else:
            if "SLURM_PARTITION" not in option_dic:
                print('unsupported job type for partition default')
                log('unsupported job type for partition default')
                sys.exit()
            else:
                donau_command += "-I "
        if "SLURM_NTASKS" in option_dic:
            donau_command += '-N ' + option_dic["SLURM_NTASKS"] + ' '
        if "SLURM_PARTITION" in option_dic:
            donau_command += '-q ' + '"' + option_dic["SLURM_PARTITION"] + '" '
        if "BEGIN" in option_dic:
            donau_command += '-S "' + time_format(option_dic["BEGIN"]) + '" '
        if "COMMENT" in option_dic:
            donau_command += '-d "' + option_dic["COMMENT"] + '" '
        if "SLURM_DEPENDENCY" in option_dic:
            donau_command += '-D ' + split_dependency(option_dic["SLURM_DEPENDENCY"]) + ' '
        if "SLURM_WORKING_DIR" in option_dic:
            donau_command += '-EP "' + option_dic["SLURM_WORKING_DIR"] + '" '
        if "SLURM_EXPORT_ENV" in option_dic:
            donau_command += '-x "' + option_dic["SLURM_EXPORT_ENV"] + '" '
        if "SLURM_STDERRMODE" in option_dic:
            option_dic["SLURM_STDERRMODE"]=option_dic["SLURM_STDERRMODE"].replace('%j','%J')
            option_dic["SLURM_STDERRMODE"]=option_dic["SLURM_STDERRMODE"].replace('%t','%I')
            option_dic["SLURM_STDERRMODE"]=option_dic["SLURM_STDERRMODE"].replace('%u','%U')
            if output_mode:
                donau_command += '-e "' + option_dic["SLURM_STDERRMODE"] + '" '
            else:
                donau_command += '-eo "' + option_dic["SLURM_STDERRMODE"] + '" '
        if "GID" in option_dic and option_dic["GID"].isdigit():
            donau_command += '-ug "' + option_dic["GID"] + '" '
        if "SLURM_EPILOG" in option_dic:
            donau_command += '-postH "' + option_dic["SLURM_EPILOG"] + '" '
        if "SLURM_JOB_NAME" in option_dic:
            donau_command += '--name "' + option_dic["SLURM_JOB_NAME"] + '" '
        if "SLURM_STDOUTMODE" in option_dic:
            option_dic["SLURM_STDOUTMODE"]=option_dic["SLURM_STDOUTMODE"].replace('%j','%J')
            option_dic["SLURM_STDOUTMODE"]=option_dic["SLURM_STDOUTMODE"].replace('%t','%I')
            option_dic["SLURM_STDOUTMODE"]=option_dic["SLURM_STDOUTMODE"].replace('%u','%U')
            if output_mode:
                donau_command += '-o "' + option_dic["SLURM_STDOUTMODE"] + '" '
            else:
                donau_command += '-oo "' + option_dic["SLURM_STDOUTMODE"] + '" '
        if "PRIORITY" in option_dic:
            donau_command += '-p ' + option_dic["PRIORITY"] + ' '
        if "SLURM_PROLOG" in option_dic:
            donau_command += '-preH "' + option_dic["SLURM_PROLOG"] + '" '
        if "SLURM_TIMELIMIT" in option_dic:
            donau_command += '-T "' + convert_timeout(option_dic["SLURM_TIMELIMIT"]) + '" '
        if "SLURM_EXCLUSIVE" in option_dic:
            donau_command += '-ex job '
        if "SRUN_CPUS_PER_TASK" in option_dic and "SLURM_GPUS_PER_TASK" in option_dic:
            donau_command += '-R "cpu=' + str(option_dic["SRUN_CPUS_PER_TASK"]) + ";gpu=" + str(
                option_dic["SLURM_GPUS_PER_TASK"]) + '" '
        elif "SRUN_CPUS_PER_TASK" in option_dic:
            donau_command += '-R "cpu=' + str(option_dic["SRUN_CPUS_PER_TASK"]) + '" '
        elif "SLURM_GPUS_PER_TASK" in option_dic:
            donau_command += '-R "gpu=' + str(option_dic["SLURM_GPUS_PER_TASK"]) + '" '
        if "SRUN_NODELIST" in option_dic and "SRUN_EXCLUDE" in option_dic:
            pattern1 = re.compile(".+\[\d+-\d+(,\d)*\]")
            pattern2 = re.compile("[A-Za-z@_0-9-.]+((,| )[A-Za-z@_0-9-.]*)*")
            m_node1 = pattern1.match(option_dic["SRUN_NODELIST"])
            m_exclude1 = pattern1.match(option_dic["SRUN_EXCLUDE"])
            m_node2 = pattern2.match(option_dic["SRUN_NODELIST"])
            m_exclude2 = pattern2.match(option_dic["SRUN_EXCLUDE"])
            if m_node1 and m_node1.group(0) == option_dic["SRUN_NODELIST"] and m_exclude1 and m_exclude1.group(0) == \
                    option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]) + " !" + str(option_dic["SRUN_EXCLUDE"]) + "' "
            elif m_node1 and m_node1.group(0) == option_dic["SRUN_NODELIST"] and m_exclude2 and m_exclude2.group(0) == \
                    option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]) + " "
                if ',' in str(option_dic["SRUN_EXCLUDE"]):
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split(','):
                        if item:
                            donau_command += '!' + item + ' '
                else:
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split():
                        if item:
                            donau_command += '!' + item + ' '
                donau_command += "' "
            elif m_node2 and m_node2.group(0) == option_dic["SRUN_NODELIST"] and m_exclude1 and m_exclude1.group(0) == \
                    option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]).replace(',', ' ')
                donau_command += " !" + str(option_dic["SRUN_EXCLUDE"]) + "' "
            elif m_node2 and m_node2.group(0) == option_dic["SRUN_NODELIST"] and m_exclude2 and m_exclude2.group(0) == \
                    option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]).replace(',', ' ')
                donau_command += ' '
                if ',' in str(option_dic["SRUN_EXCLUDE"]):
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split(','):
                        if item:
                            donau_command += '!' + item + ' '
                else:
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split():
                        if item:
                            donau_command += '!' + item + ' '
                donau_command += "' "
            else:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]) + " !" + str(option_dic["SRUN_EXCLUDE"]) + "' "
        elif "SRUN_NODELIST" in option_dic:
            pattern1 = re.compile(".+\[\d+-\d+(,\d)*\]")
            m1 = pattern1.match(option_dic["SRUN_NODELIST"])
            pattern2 = re.compile("[A-Za-z@_0-9-.]+((,| )[A-Za-z@_0-9-.]*)*")
            m2 = pattern2.match(option_dic["SRUN_NODELIST"])
            if m1 and m1.group(0) == option_dic["SRUN_NODELIST"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]) + "' "
            elif m2 and m2.group(0) == option_dic["SRUN_NODELIST"]:
                donau_command += "-pn '" + str(option_dic["SRUN_NODELIST"]).replace(',', ' ') + "' "
        elif "SRUN_EXCLUDE" in option_dic:
            pattern1 = re.compile(".+\[\d+-\d+(,\d)*\]")
            m1 = pattern1.match(option_dic["SRUN_EXCLUDE"])
            pattern2 = re.compile("[A-Za-z@_0-9-.]+((,| )[A-Za-z@_0-9-.]*)*")
            m2 = pattern2.match(option_dic["SRUN_EXCLUDE"])
            if m1 and m1.group(0) == option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '!" + str(option_dic["SRUN_EXCLUDE"]) + "' "
            elif m2 and m2.group(0) == option_dic["SRUN_EXCLUDE"]:
                donau_command += "-pn '"
                if ',' in str(option_dic["SRUN_EXCLUDE"]):
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split(','):
                        if item:
                            donau_command += '!' + item + ' '
                else:
                    for item in str(option_dic["SRUN_EXCLUDE"]).strip().split():
                        if item:
                            donau_command += '!' + item + ' '
                donau_command += "' "
        if args == []:
            print('srun: fatal: No command given to execute.')
            log('srun: fatal: No command given to execute.')
            sys.exit()
        else:
            for item in args:
                donau_command += " " + item
            log(donau_command)
            if blocked:
                process = subprocess.Popen(donau_command, shell=True, stdout=subprocess.PIPE, bufsize=1)
                for item in iter(process.stdout.readline, b''):
                    print(item.decode().replace('\n', ''))
                    log(item.decode().replace('\n', ''))
            else:
                os.system(donau_command)
    except getopt.GetoptError as err:
        invalid_option = get_invalid_option(str(err))
        print_invalid_info(invalid_option)
        sys.exit()
    except KeyboardInterrupt:
        if process is not None:
            if process.returncode is None:
                print("\nJob continues to run backend, use squeue to query job status")
                log("^C\nJob continues to run backend, use squeue to query job status")
            else:
                sys.exit(process.returncode)
    except:
        sys.exit()
